import sys,time,base64,hmac,hashlib,struct

from PyQt5 import QtCore, QtGui, QtWidgets

from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QMainWindow, QProgressBar, QMessageBox,QFileDialog

from datetime import datetime

import json

from pyzbar.pyzbar import decode

from PIL import Image

from urllib.parse import unquote

from src.MyQrcode import MyQrcode

import concurrent.futures

import pyotp


def calGoogleCode(secretKey):
    if (len(secretKey) == 52):
        # 创建一个TOTP对象
        totp = pyotp.TOTP(secretKey)
        # 获取当前的时间一次性密码
        googleCode = totp.now()
    else:
        input = int(time.time()) // 30
        key = base64.b32decode(secretKey)
        msg = struct.pack(">Q", input)
        googleCode = hmac.new(key, msg, hashlib.sha1).digest()
        #版本判断
        if (sys.version_info> (2, 7)):
            o = googleCode[19] & 15
        else:
            o = ord(googleCode[19]) & 15
        googleCode = str((struct.unpack(">I", googleCode[o:o + 4])[0] & 0x7fffffff) % 1000000)
        # if len(googleCode) == 5:
        #     googleCode = '0' + googleCode
        # 如果验证码的第一位是0，则不会显示。此处判断若是5位码，则在第一位补上0
        googleCode = googleCode.zfill(6)
    return googleCode





class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("google auth")
        Dialog.resize(800, 600)

        self.secret_input = QtWidgets.QLineEdit(Dialog)
        self.secret_input.setGeometry(QtCore.QRect(360, 519, 211, 40))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(14)
        self.secret_input.setFont(font)
        self.secret_input.setObjectName("secret_input")
        self.secret_label = QtWidgets.QLabel(Dialog)
        self.secret_label.setGeometry(QtCore.QRect(280, 520, 60, 30))
        self.secret_label.setMinimumSize(QtCore.QSize(60, 30))
        self.secret_label.setMaximumSize(QtCore.QSize(60, 30))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(12)
        self.secret_label.setFont(font)
        self.secret_label.setObjectName("secret_label")
        self.addbutton = QtWidgets.QPushButton(Dialog)
        self.addbutton.setGeometry(QtCore.QRect(590, 510, 80, 60))
        self.addbutton.setMinimumSize(QtCore.QSize(80, 60))
        self.addbutton.setMaximumSize(QtCore.QSize(80, 60))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(12)
        self.addbutton.setFont(font)
        self.addbutton.setObjectName("addbutton")

        self.importbutton = QtWidgets.QPushButton(Dialog)
        self.importbutton.setGeometry(QtCore.QRect(680, 510, 80, 60))
        self.importbutton.setMinimumSize(QtCore.QSize(80, 60))
        self.importbutton.setMaximumSize(QtCore.QSize(80, 60))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(12)
        self.importbutton.setFont(font)
        self.importbutton.setObjectName("importbutton")

        self.account_label = QtWidgets.QLabel(Dialog)
        self.account_label.setGeometry(QtCore.QRect(40, 520, 60, 30))
        self.account_label.setMinimumSize(QtCore.QSize(60, 30))
        self.account_label.setMaximumSize(QtCore.QSize(60, 30))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(12)
        self.account_label.setFont(font)
        self.account_label.setObjectName("account_label")

        self.account_input = QtWidgets.QLineEdit(Dialog)
        self.account_input.setGeometry(QtCore.QRect(100, 520, 180, 40))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(14)
        self.account_input.setFont(font)
        self.account_input.setObjectName("account_input")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "google auth"))
        self.secret_label.setText(_translate("Dialog", " 秘 钥"))
        self.addbutton.setText(_translate("Dialog", "新增"))
        self.importbutton.setText(_translate("Dialog", "扫码"))
        self.account_label.setText(_translate("Dialog", "账 号"))


class MyMainForm(QMainWindow, Ui_Dialog):
    def __init__(self, parent=None):
        super(MyMainForm, self).__init__(parent)
        self.setupUi(self)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.scheduleTask)
        self.timer.start(1000)  # 每1000毫秒（1秒）执行一次
        self.addbutton.clicked.connect(self.addConfig)
        self.importbutton.clicked.connect(self.importQrCode)
        self.currentSize = 0 # 已配置了几个
        self.configFile = {}
        self.progress_bar = QProgressBar(self)
        self.myqrcode = MyQrcode()

        self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=3)

        self.initConfig()
        self.refreshContent()

    # 定时任务
    def scheduleTask(self):
        seconds = datetime.now().second
        if self.progress_bar.value() > 0:
            self.progress_bar.setValue(self.progress_bar.value() - 3.333)

        if (seconds % 30 == 0):
            self.refreshContent()
            self.progress_bar.setValue(100)

    # 刷新code内容
    def refreshContent(self):
        # 读取json文件获取 已配置的信息
        for i in range(len(self.configFile["data"])):
            code = calGoogleCode(self.configFile["data"][i]["secret"])

            getattr(self, "code_out" + "@" + str(i)).setText(code)
            getattr(self, "account_out" + "@" + str(i)).setText(self.configFile["data"][i]["account"])

    # 新增配置
    def addConfig(self):
        try:
            if (self.account_input.text() == "" or (len(self.secret_input.text()) != 16 and len(self.secret_input.text()) != 52)):
                QMessageBox.information(None, '提示', '账号不能为空，且秘钥长度必须为16位', QMessageBox.Ok)
                return
            calGoogleCode(self.secret_input.text())
            newConfig = {"account":self.account_input.text(),"secret": self.secret_input.text()}
            self.configFile["data"].append(newConfig)

            # 写入到文件
            with open("data.json", "w") as json_file:
                json.dump(self.configFile, json_file)

            self.addSingleForm(len(self.configFile["data"]) - 1)
            self.refreshContent()
            self.account_input.setText("")
            self.secret_input.setText("")
        except Exception as e:
            print(e)


    # 初始化配置
    def initConfig(self):
        self.progress_bar.setGeometry(30, 8, 560,15)
        self.progress_bar.setValue((30 - datetime.now().second % 30)/0.3)

        try:
            with open('data.json', 'r') as file:
                self.configFile = json.load(file)
        except Exception as e:
            self.configFile = {"account_out_hight": 60, "account_out_width": 300, "code_out_hight": 60, "code_out_width": 200,"data":[]}
            # 写入到文件
            with open("data.json", "w") as json_file:
                json.dump(self.configFile, json_file)



        for i in range(len(self.configFile["data"])):
            self.addSingleForm(i)

    # 动态添加表单
    def addSingleForm(self, index):

        _translate = QtCore.QCoreApplication.translate
        setattr(self, "code_out" + "@" + str(index),QtWidgets.QTextBrowser(self))
        getattr(self, "code_out" + "@" + str(index)).setGeometry(QtCore.QRect(360, 40 + 90 * index, 100, 90))
        getattr(self, "code_out" + "@" + str(index)).setMinimumSize(QtCore.QSize(self.configFile["code_out_width"], self.configFile["code_out_hight"]))
        getattr(self, "code_out" + "@" + str(index)).setMaximumSize(QtCore.QSize(self.configFile["code_out_width"], self.configFile["code_out_hight"]))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(16)
        getattr(self, "code_out" + "@" + str(index)).setFont(font)
        getattr(self, "code_out" + "@" + str(index)).setObjectName("code_out" + "@" +  str(index))
        getattr(self, "code_out" + "@" + str(index)).setHtml(_translate("Dialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Adobe Arabic\'; font-size:16pt; font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'SimSun\'; font-size:9pt;\"><br /></p></body></html>"))


        setattr(self, "account_out" + "@" + str(index),QtWidgets.QTextBrowser(self))
        getattr(self, "account_out" + "@" + str(index)).setGeometry(QtCore.QRect(30, 40 + 90 * index, 200, 90))
        getattr(self, "account_out" + "@" + str(index)).setMinimumSize(QtCore.QSize(self.configFile["account_out_width"], self.configFile["account_out_hight"]))
        getattr(self, "account_out" + "@" + str(index)).setMaximumSize(QtCore.QSize(self.configFile["account_out_width"], self.configFile["account_out_hight"]))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(16)
        getattr(self, "account_out" + "@" + str(index)).setFont(font)
        getattr(self, "account_out" + "@" + str(index)).setObjectName("account_out" + "@" + str(index))
        getattr(self, "account_out" + "@" + str(index)).setHtml(_translate("Dialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Adobe Arabic\'; font-size:16pt; font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:\'SimSun\'; font-size:9pt;\"><br /></p></body></html>"))


        setattr(self, "copy_button" + "@" + str(index), QtWidgets.QPushButton(self))
        getattr(self, "copy_button" + "@" + str(index)).setGeometry(QtCore.QRect(600, 40 + 90 * index, 70, 60))
        getattr(self, "copy_button" + "@" + str(index)).setMinimumSize(QtCore.QSize(70, 60))
        getattr(self, "copy_button" + "@" + str(index)).setMaximumSize(QtCore.QSize(70, 60))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(12)
        getattr(self, "copy_button" + "@" + str(index)).setFont(font)
        getattr(self, "copy_button" + "@" + str(index)).setObjectName("addbutton")
        getattr(self, "copy_button" + "@" + str(index)).setText(_translate("Dialog", "复制"))

        setattr(self, "del_button" + "@" + str(index), QtWidgets.QPushButton(self))
        getattr(self, "del_button" + "@" + str(index)).setGeometry(QtCore.QRect(680, 40 + 90 * index, 70, 60))
        getattr(self, "del_button" + "@" + str(index)).setMinimumSize(QtCore.QSize(70, 60))
        getattr(self, "del_button" + "@" + str(index)).setMaximumSize(QtCore.QSize(70, 60))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(12)
        getattr(self, "del_button" + "@" + str(index)).setFont(font)
        getattr(self, "del_button" + "@" + str(index)).setObjectName("addbutton")
        getattr(self, "del_button" + "@" + str(index)).setText(_translate("Dialog", "删除"))

        getattr(self, "code_out" + "@" + str(index)).show()
        getattr(self, "account_out" + "@" + str(index)).show()
        getattr(self, "del_button" + "@" + str(index)).show()
        getattr(self, "copy_button" + "@" + str(index)).show()

        getattr(self, "del_button" + "@" + str(index)).clicked.connect(lambda: self.deleteConfig(index))
        getattr(self, "copy_button" + "@" + str(index)).clicked.connect(lambda: self.copyCode(index))

        tipsButton =  QtWidgets.QPushButton(self)
        tipsButton.setStyleSheet("QPushButton {"
                         "background-image: url(path_to_your_image.png);"
                         "border: none;"
                         "}"
                         "QPushButton:hover {"
                         "background-image: url(path_to_your_hover_image.png);"
                         "}")
        setattr(self, "tips" + "@" + str(1),tipsButton)
        getattr(self, "tips" + "@" + str(1)).setGeometry(QtCore.QRect(300, 300, 150, 60))
        getattr(self, "tips" + "@" + str(1)).setMinimumSize(QtCore.QSize(150, 60))
        getattr(self, "tips" + "@" + str(1)).setMaximumSize(QtCore.QSize(150, 60))
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(12)
        getattr(self, "tips" + "@" + str(1)).setFont(font)
        getattr(self, "tips" + "@" + str(1)).setObjectName("tips")
        getattr(self, "tips" + "@" + str(1)).setText(_translate("Dialog", "复制成功"))

        getattr(self,"tips" + "@" + str(1)).hide()

    # 删除配置
    def deleteConfig(self, index):
        reply = QMessageBox.question(None, '确认', '您确定要执行此操作吗？',
                                 QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        if reply == QMessageBox.Yes:
            getattr(self,"code_out" + "@" + str(len(self.configFile["data"]) - 1)).hide()
            getattr(self,"account_out" + "@" + str(len(self.configFile["data"]) - 1)).hide()
            getattr(self,"del_button" + "@" + str(len(self.configFile["data"]) - 1)).hide()
            getattr(self,"copy_button" + "@" + str(len(self.configFile["data"]) - 1)).hide()
            delattr(self,"code_out" + "@" + str(len(self.configFile["data"]) - 1))
            delattr(self,"account_out" + "@" + str(len(self.configFile["data"]) - 1))
            delattr(self,"del_button" + "@" + str(len(self.configFile["data"]) - 1))
            delattr(self,"copy_button" + "@" + str(len(self.configFile["data"]) - 1))
            self.configFile["data"].pop(index)

            # 写入到文件
            with open("data.json", "w") as json_file:
                json.dump(self.configFile, json_file)
            self.refreshContent()

    def decode_qr_code(self, qr_image_path):
        image = Image.open(qr_image_path)
        decoded_objects = decode(image)

        # 假设只有一个解码结果，且我们只关心解码出的数据
        data = decoded_objects[0].data.decode('utf-8')
        print(data)
        # 提取URL
        return data


    # 导入二维码
    def importQrCode(self):
        # 弹出文件选择对话框，并获取选择的文件路径
        try:
            file_path = QFileDialog.getOpenFileName(self, 'Open File', '', 'All Files (*)')
            if file_path[0]:  # 如果用户选择了文件
                # data = self.decode_qr_code("test.jpg")
                account, secret = self.myqrcode.getSecretAndAccount(file_path[0])
                self.secret_input.setText(secret)
                self.account_input.setText(account)
                self.addConfig()
        except Exception as e:
            print(e)
            QMessageBox.information(None, '提示', '导入失败，请检查二维码是否正确！', QMessageBox.Ok)

    # 复制到剪切板
    def copyCode(self, index):
        t = getattr(self, "code_out" + "@" + str(index)).toPlainText()
        clipboard = QApplication.clipboard()
        clipboard.setText(t)
        self.copyMsg(1)
        self.executor.submit(self.hideMsg)


    def hideMsg(self):
        time.sleep(1.5)  # 假设任务需要3秒钟完成
        getattr(self,"tips" + "@" + str(1)).hide()

    def copyMsg(self, t):
        if t == 1 :
            getattr(self,"tips" + "@" + str(1)).show()





if __name__ == "__main__":
    #固定的，PyQt5程序都需要QApplication对象。sys.argv是命令行参数列表，确保程序可以双击运行
    app = QApplication(sys.argv)
    #初始化
    myWin = MyMainForm()
    #将窗口控件显示在屏幕上
    myWin.show()
    #程序运行，sys.exit方法确保程序完整退出。
    sys.exit(app.exec_())